package aysnowflake

import (
	"errors"
	"strconv"
	"sync"
	"time"
)

/*
 * SnowFlake的结构:
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
 * 1位标识，正数是0，负数是1，一般选择0
 * 41位时间截(毫秒级)，(2 ^ 41)-1 =2199023255551 ms ，除以(1000*60*60*24*365)ms /年= 69年
 * 10位的数据机器位，datacenterId和workerId各一半，数据中心和机器分别可以部署512个节点，512 * 512 = 262144 个节点
 * 12位序列，毫秒内的计数，12位的计数顺序号支持每个节点每毫秒(同一机器，同一时间截)产生4096个ID序号
 * SnowFlake的优点是，整体上按照时间自增排序，并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)
 * 经测试，SnowFlake每秒能够产生26万ID左右。
 */
const (
	//开始时间戳 2021-01-01
	/*
		可以通过该方式生成所需的时间数字：
			sdate := "2021-01-01"
			tt, _ := time.ParseInLocation("2006-01-02", sdate, time.Local)
			fmt.Println(tt.UnixNano() / 1e6)
	*/
	epoch int64 = 1609430400000
	// 机器id & 数据中心id & 序列号 -> 位数
	workerIdBits, datacenterIdBits, sequenceBits int64 = 5, 5, 5
	//支持的最大机器id，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
	maxWorkerId int64 = -1 ^ (-1 << workerIdBits)
	// 支持的最大数据标识id，结果是31
	maxDatacenterId int64 = -1 ^ (-1 << datacenterIdBits)
	// 机器ID向左移12位
	workerIdShift int64 = sequenceBits
	// 数据标识id向左移17位
	datacenterIdShift int64 = sequenceBits + workerIdBits
	// 时间截向左移22位
	timestampLeftShift int64 = sequenceBits + workerIdBits + datacenterIdBits
	// 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095)
	sequenceMask int64 = -1 ^ (-1 << sequenceBits)
)

/*
 * 构造
 */
type SnowflakeIdWorker struct {
	mutex         sync.Mutex // 添加互斥锁 确保并发安全
	lastTimestamp int64      // 上次生成ID的时间截
	workerId      int64      // 工作机器ID (2^5-1 -> 0~31)
	datacenterId  int64      // 数据中心ID (2^5-1 -> 0~31)
	sequence      int64      // 每毫秒生成序列 (2^12-1 - >0~4095)
}

/*
 * 创建Snowflake工具
 * workerId 工作ID (0~31)
 * datacenterId 数据中心ID (0~31)
 */
func InitSnowFlake(wId int64, dId int64) (*SnowflakeIdWorker, error) {
	if wId < 0 || wId > maxWorkerId {
		return nil, errors.New("The worker id of illegal")
	}
	if dId < 0 || dId > maxDatacenterId {
		return nil, errors.New("The datacenter id of illegal")
	}
	// 生成一个新节点
	return &SnowflakeIdWorker{
		lastTimestamp: 0,
		workerId:      wId,
		datacenterId:  dId,
		sequence:      0,
	}, nil
}

/*
 * 获取ID，返回-1代表时钟被回滚，无法生效需要注意
 */
func (w *SnowflakeIdWorker) GetId() int64 {
	// 加锁保证线程安全
	w.mutex.Lock()
	defer w.mutex.Unlock()

	// 获取生成时的时间戳 毫秒
	now := time.Now().UnixNano() / 1e6
	// 检查系统时间是否被回滚，回滚需要报错
	if now < w.lastTimestamp {
		errors.New("Clock moved backwards")
		// 时钟错误码
		return -1
	}

	// 同一时间
	if w.lastTimestamp == now {
		w.sequence = (w.sequence + 1) & sequenceMask
		if w.sequence == 0 { // 阻塞到下一个毫秒，直到获得新的时间戳
			for now <= w.lastTimestamp {
				now = time.Now().UnixNano() / 1e6
			}
		}
	} else { // 时间进入下一阶段，重置工作节点生成ID的序号
		w.sequence = 0
	}
	// 更新为当前时间，用于下次检查
	w.lastTimestamp = now

	// 生成id
	ID := int64((now-epoch)<<timestampLeftShift | w.datacenterId<<datacenterIdShift | (w.workerId << workerIdShift) | w.sequence)
	return ID
}

/*
 * 转换成二进制字符串
 */
func ConvertToString(num int64) string {
	if num == 0 { // 特殊情况
		return "0"
	}
	res := ""
	for num > 0 {
		res = strconv.FormatInt(num&1, 10) + res
		num >>= 1 // 右移一位，效果等同于 num /= 2
	}
	return res
}
